package com.qzh.JDBC;

import cn.hutool.core.util.ObjectUtil;
import com.qzh.JDBC.annotation.EasyEntity;
import com.qzh.JDBC.annotation.EasyField;
import com.qzh.summer.SummerException.SummerException;
import com.qzh.utils.ReflectUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class EasyDB {
    /**
     * 当前是否就绪。
     */
      private boolean isActive=false;
    /**
     * 当前连接的URL
     */
    private String url;
    /**
     * 当前连接的USER
     */
      private String user;
    /**
     * 当前连接的PASSWORD
     */
      private String password;
    /**
     * 当前存活的连接
     */
    private Connection connection;
      private String textSQL = "select 1 as result from dual";
    /**
     * 包扫描器
     */
    private Scanner scanner;
    private Map<Class, Map<String, Scanner.OldAndNewName>> classFieldMap ;
    /**
     * 类初始化方法，只能执行一次。
     * @param user
     * @param password
     * @return
     */
   public boolean init(String url,String  user,String password){
                if(isActive){
                    return true;
                }
       try {
           Class.forName("com.mysql.cj.jdbc.Driver");
       } catch (ClassNotFoundException e) {
           throw new RuntimeException(e);
       }
       try {
           Connection conn = DriverManager.getConnection(url, user, password);
          Statement statement = conn.createStatement();
           ResultSet resultSet = statement.executeQuery(textSQL);
          if(ObjectUtil.isNotNull(resultSet)){
             if(resultSet.next()){
              //结果不为空，代表能够正确查询
                 this.isActive  =true;
                 this.connection = conn;
              this.url=url;
              this.password = password;
              this.user = user;
              //启用scanner
                 List<Class> classList;
                 try {
                      scanner =new Scanner();
                     classList =  scanner.scanPackage("com.qzh");
                     classFieldMap = scanner.DBNameFilter(EasyField.class);
                     classFieldMap.size();
                 } catch (IOException e) {
                     throw new RuntimeException(e);
                 } catch (ClassNotFoundException e) {
                     throw new RuntimeException(e);
                 } catch (URISyntaxException e) {
                     throw new RuntimeException(e);
                 }
                 System.out.println("EasyDB is active");
             }
          }
       } catch (SQLException e) {
           throw new RuntimeException(e);
       }
       return false;
   }

    /**
     * SQL的SELECT语句，不能直接执行，需要进行防止SQL注入操作
     * 保证完全安全的情况下可以运行。
     * @param clazz 有DBField注解的类
     * @param sql sql语句
     * @return
     * @param <T>
     */
   public <T> List<T> Select(Class<T> clazz , String sql) throws SummerException {
       List<T> result = new ArrayList<>();
       Map<String , Scanner.OldAndNewName> clzfieldMap = classFieldMap.get(clazz);
       if(!clazz.isAnnotationPresent(EasyEntity.class)){
           throw new SummerException("该类无'DBField'注解");
       }
       Statement statement;
       try {
         statement =   connection.createStatement();
       } catch (SQLException e) {
           throw new RuntimeException(e);
       }
       ResultSet resultSet;
       try {
           resultSet = statement.executeQuery(sql);
           if(ObjectUtil.isNotNull(resultSet)){
               //需要判空。ResultSet的数据是需要根据名字取的，这个时候就需要classFieldData了
               Field[] fields  = clazz.getDeclaredFields();
               Map<String,Field>fieldMap = new HashMap<>();
               for (Field field : fields) {
                   field.setAccessible(true);
                   fieldMap.put(field.getName(),field);
               }
                while(resultSet.next()){
                    T o= ReflectUtils.createNewObject(clazz);
                    ResultSetMetaData resultSetMetaData=  resultSet.getMetaData();
                    int columnCount = resultSetMetaData.getColumnCount();
                    for (int i = 1; i <=columnCount; i++) {
                        //数据库new name
                        String name = resultSetMetaData.getColumnName(i);
                        //实体类old name
                        String oldName;
                        try{
                             oldName=  clzfieldMap.get(name).getOldName();
                        }catch(Exception e){
                        throw new SummerException(clazz.getName()+"类的"+name+"注解不匹配");
                        }


                        int type = resultSetMetaData.getColumnType(i);
                        int scale = resultSetMetaData.getScale(i);
                        switch (type){
                            case Types.TINYINT:Integer integer = resultSet.getInt(name);
                                fieldMap.get(oldName).set(o,integer);break;
                            case Types.SMALLINT:Integer integer1 = resultSet.getInt(name);
                                fieldMap.get(oldName).set(o,integer1);break;
                            case Types.INTEGER:
                                Integer integer2 = resultSet.getInt(name);
                                fieldMap.get(oldName).set(o,integer2);break;
                            case Types.BIGINT:Long aLong = resultSet.getLong(name);
                                fieldMap.get(oldName).set(o,aLong);break;
                            case Types.DECIMAL: BigDecimal bigDecimal = resultSet.getBigDecimal(name,scale);
                                fieldMap.get(oldName).set(o,bigDecimal);break;
                            case Types.VARCHAR:String s = resultSet.getString(name);
                                fieldMap.get(oldName).set(o,s);break;
                            case Types.BIT:Boolean aBoolean = resultSet.getBoolean(name);
                                fieldMap.get(oldName).set(o,aBoolean);break;
                        }
                    }
                            result.add(o);

                }
                return  result;

           }
           else{
               return result;
           }

       } catch (SQLException e) {
           throw new RuntimeException(e);
       } catch (InstantiationException e) {
           throw new RuntimeException(e);
       } catch (IllegalAccessException e) {
           throw new RuntimeException(e);
       }

   }

}
